﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using library;
using System.Net;
using System.Globalization;
using System.IO;

namespace app
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int port = Utils.GetAvaiablePort();

            Client.Start(Utils.GetAddress(), port);

            txtMain.Text = Client.GetWelcomeKey();

            Log.OnLog += Log_OnLog;

            Client.OnFileUpload += Files_OnFileUpload;

            Client.OnSearchReturn += Client_onSearchReturn;

            this.Text = Convert.ToBase64String(
                (new byte[] { 127, 0, 0, 1, BitConverter.GetBytes(port)[0], BitConverter.GetBytes(port)[1] }));

            this.AllowDrop = true;

            this.DragEnter += new DragEventHandler(Form1_DragEnter);

            this.DragDrop += new DragEventHandler(Form1_DragDrop);
        }

        void Files_OnFileUpload(string filename, string address)
        {
            txtLog.Invoke(() => { txtLog.AppendText(filename + ": " + address + "\r\n"); });
        }

        void Form1_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy;
        }

        void Form1_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

            foreach (string file in files)
                Client.Upload(file);
        }
        /*
        static byte[] GenerateMetadata(string path, byte[] target_address)
        {
            var metadata = new Dictionary<byte[], byte[]>();

            var filename = Path.GetFileName(path);

            Metadata m = new Metadata();

            metadata.Add(Encoding.Unicode.GetBytes("filename"), Encoding.Unicode.GetBytes(filename));

            List<byte> buffer = new List<byte>();

            foreach (byte[] key in metadata.Keys)
            {
                byte[] metadata_item = BitConverter.GetBytes(key.Length).
                    Concat(key).
                    Concat(BitConverter.GetBytes(metadata[key].Length)).
                    Concat(metadata[key]).ToArray();

                buffer.AddRange(metadata_item);
            }

            byte[] data = new byte[Parameters.packetHeaderSize + Parameters.addressSize + buffer.Count()];

            buffer.CopyTo(data, Parameters.packetHeaderSize + Parameters.addressSize);

            target_address.CopyTo(data, Parameters.packetHeaderSize);

            data[0] = (byte)PacketTypes.Metadata;

            Utils.ComputeHash(data, Parameters.packetHeaderSize, data.Length - Parameters.packetHeaderSize).CopyTo(data, 1);

            LocalPackets.Add(address, data, Client.LocalPeer);

            return address;

            //data = LocalData.Get(baseAddress);
            // if (data != null)
            //     LocalIndex.ParseContent(address, data);
        }
        */
        void Log_OnLog(string log)
        {
            if(!txtLog.IsDisposed)
                txtLog.Invoke(() => { txtLog.Text = log + txtLog.Text.Substring(0, Math.Min(10000, txtLog.Text.Length)); });
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Log.OnLog -= Log_OnLog;

            Client.Close();
        }

        private void txtMain_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter)
            {
                //welcome key
                if (txtMain.Text.Length == 8)
                    Client.GetPeer(txtMain.Text);
                else if (txtMain.Text.Length > 0)
                {
                    IPEndPoint ipe = CreateIPEndPoint(txtMain.Text);

                    if (ipe != null)
                        txtMain.Text = Convert.ToBase64String(Addresses.ToBytes(ipe));
                    else
                        if (txtMain.Text.Length == 44)
                        {
                            if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                                Client.Download(txtMain.Text, saveFileDialog1.FileName);
                        }
                        else
                        {
                            //Client.Search(txtMain.Text);
                        }
                }

                else
                {

                    txtMain.Text = Client.GetWelcomeKey();
                }
            }
        }

        void Client_onSearchReturn(string search, ValueHits<p2pPost> data)
        {
            txtReturn.Invoke(() => { txtReturn.AppendText(search + ": " + data + "\r\n"); });
        }

        public static IPEndPoint CreateIPEndPoint(string endPoint)
        {
            string[] ep = endPoint.Split(':');
            if (ep.Length != 2) return null;
            IPAddress ip;
            if (!IPAddress.TryParse(ep[0], out ip))
            {
                return null;
            }
            int port;
            if (!int.TryParse(ep[1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
            {
                return null;
            }
            return new IPEndPoint(ip, port);
        }


        internal static string points(byte[] data)
        {
            List<string> s = new List<string>();

            foreach (byte b in data)
            {
                s.Add(b.ToString().PadLeft(3, '0'));
            }

            return string.Join(".", s);
        }

        public void LoadLogOptions()
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Dictionary<int, double> peers = Client.GetPeers();

            flowLayoutPanel1.Controls.Clear();

            foreach(int key in peers.Keys)
            {
                Button b = new Button();

                b.AutoSize = true;

                b.Text = key.ToString() + ":" + peers[key].ToString();

                flowLayoutPanel1.Controls.Add(b);
            }

        }

        private void timer_stats_Tick(object sender, EventArgs e)
        {
            stripDownload.Text = "Download: " +
                                 stats(Client.Stats.Received.Median, Client.Stats.max_download);

            stripUpload.Text = "Upload: " +
                     stats(Client.Stats.Sent.Median, Client.Stats.max_upload);
        }

        string stats(double bytes, int max)
        {
            return string.Format("{0}/s {1}", Utils.DisplayBytes((long) bytes), 
                
                string.Format("({0}/s)", Utils.DisplayBytes(max)));
        }

        private void uploadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var pos = stripUpload.DropDownItems.IndexOf((ToolStripMenuItem)sender);

            var max = 0;

            switch(pos)
            {
                case 1: max = 1024 * 1024 * 10; break;

                case 2: max = 1024 * 1024; break;

                case 3: max = 512 * 1024; break;

                case 4: max = 256 * 1024; break;

                case 5: max = 128 * 1024; break;

                case 6: max = 64 * 1024; break;
            }

            Client.Stats.max_upload = max;
        }

        private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var pos = stripDownload.DropDownItems.IndexOf((ToolStripMenuItem)sender);

            var max = 0;

            switch (pos)
            {
                case 1: max = 1024 * 1024 * 10; break;

                case 2: max = 1024 * 1024; break;

                case 3: max = 512 * 1024; break;

                case 4: max = 256 * 1024; break;

                case 5: max = 128 * 1024; break;

                case 6: max = 64 * 1024; break;
            }

            Client.Stats.max_download = max;

        }
    }

    public static class ControlExtensions
    {
        public static void Invoke(this Control control, Action action)
        {
            if (control.InvokeRequired)
            {
                if(!control.Disposing)
                    control.Invoke(new MethodInvoker(action), null);
            }
            else
            {
                action.Invoke();
            }
        }
    }

}
